/*
 * Copyright 2011 Daniel Drown
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * nat64d.c - tun interface setup and main event loop
 */
#include "system_headers.h"
#include "ipv4.h"
#include "ipv6.h"
#include "interfaces.h"
#include "nat64d.h"

/* function: tun_alloc
 * creates a tun interface and names it
 * dev - the name for the new tun device
 */
int tun_alloc(char *dev)
{
  struct ifreq ifr;
  int fd, err;

  if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
    return fd;

  memset(&ifr, 0, sizeof(ifr));

  ifr.ifr_flags = IFF_TUN; 
  if( *dev )
    strncpy(ifr.ifr_name, dev, IFNAMSIZ);

  if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
    close(fd);
    return err;
  }
  strcpy(dev, ifr.ifr_name);
  return fd;
}

/* function: configure_tun_ip
 * configures the ipv4 and ipv6 addresses on the tunnel interface
 * note: steals a /112 out of the (assumed routed to us) /64
 */
void configure_tun_ip() {
  struct in6_addr local_nat64_prefix_6;
  struct in_addr local_nat64_prefix_4;
  char addr_command[512], charbuffer[INET6_ADDRSTRLEN];

  local_nat64_prefix_6 = ipv6_local_subnet;
  local_nat64_prefix_6.s6_addr32[3] = htonl(1);

  local_nat64_prefix_4.s_addr = htonl(IPV4_LOCAL_SUBNET | 1);

  snprintf(addr_command, sizeof(addr_command), 
      "ip link set %s mtu 1400 up", 
      DEVICENAME);
  addr_command[sizeof(addr_command)-1] = '\0';
  printf("cmd: %s\n",addr_command);
  system(addr_command);

  snprintf(addr_command, sizeof(addr_command), 
      "ip -6 addr add %s/112 dev %s", 
      inet_ntop(AF_INET6, &local_nat64_prefix_6, charbuffer, sizeof(charbuffer)),DEVICENAME
      );
  addr_command[sizeof(addr_command)-1] = '\0';
  printf("cmd: %s\n",addr_command);
  system(addr_command);

  snprintf(addr_command, sizeof(addr_command), 
      "ip addr add %s/24 dev %s",
      inet_ntop(AF_INET, &local_nat64_prefix_4, charbuffer, sizeof(charbuffer)),DEVICENAME
      );
  addr_command[sizeof(addr_command)-1] = '\0';
  printf("cmd: %s\n",addr_command);
  system(addr_command);

  /* setup default ipv4 route */
  system("ip route replace default dev " DEVICENAME " mtu 1340");

  /* we have to forward packets from the WAN to the tun interface */
  system("echo 1 >/proc/sys/net/ipv6/conf/all/forwarding");
}

/* function: main
 * allocate and setup the tun device, then run the event loop
 * TODO: disable ipv6 forwarding on exit
 */
int main() {
  char dev[IFNAMSIZ] = DEVICENAME;
  int fd;
  char packet[PACKETLEN];
  ssize_t readlen;

  fd = tun_alloc(dev);
  if(fd < 0) {
    perror("tun_alloc failed");
    exit(1);
  }

  set_local_ipv6_prefix();

  configure_tun_ip();

  printf("ready\n");

  while((readlen = read(fd,packet,PACKETLEN)) > 0) {
    uint16_t flags, proto;

    if(readlen < 4) {
      printf("readlen short: %d\n", readlen);
      continue;
    }
    
    flags = proto = 0;
    memcpy(&flags, packet, 2);
    memcpy(&proto, packet+2, 2);

    proto = ntohs(proto);
    flags = ntohs(flags);

    if(proto == ETH_P_IP) {
      ip_packet(fd,packet+4,readlen-4);
    } else if(proto == ETH_P_IPV6) {
      ipv6_packet(fd,packet+4,readlen-4);
    } else {
      printf("unknown packet type = %x\n",proto);
    }

    memset(packet, 0, PACKETLEN);
  }

  return 0;
}
